﻿using System.Web.Mvc;
using System.Web.Routing;
using SlamCms.Web.HttpHandlers;
using SlamCms.Web.Mvc;
using SlamCms.Web.Mvc.RouteHandlers;

namespace System.Web.Mvc
{
	public static class RouteTableExtensions
	{
		public static Route MapSharePointImages(this RouteCollection routeTable, string name, string urlStartsWith, object defaults = null)
		{
			if (defaults == null)
				defaults = new { controller = "SharepointImage", action = "Index" };

			return routeTable.MapRoute(name, "{*url}", defaults, new { url = StartsWithConstraint.Create(urlStartsWith) });
		}

		public static Route MapSharePointDocuments(this RouteCollection routeTable, string name, string urlStartsWith, object defaults = null)
		{
			if (defaults == null)
				defaults = new { controller = "SharepointDocument", action = "Index" };

			return routeTable.MapRoute(name, "{*url}", defaults, new { url = StartsWithConstraint.Create(urlStartsWith) });
		}

		public static Route MapSharePointDownloads(this RouteCollection routeTable, string urlStartsWith, object defaults = null)
		{
			if (defaults == null)
				defaults = new { controller = "SharepointDocument", action = "Download", slug = UrlParameter.Optional };

			return routeTable.MapRoute("ContentItemDownload", urlStartsWith + "/{contentItemId}/{slug}", defaults, new { url = StartsWithConstraint.Create(urlStartsWith) });
		}

		public static Route MapHttpHandler<THandler>(this RouteCollection routes, string name, string url) where THandler : IHttpHandler, new()
		{
			return routes.MapHttpHandler(name, url, null, null, r => new THandler());
		}

		public static Route MapHttpHandler<THandler>(this RouteCollection routes, string name, string url, Func<RequestContext, THandler> handlerFactory) where THandler : IHttpHandler
		{
			return routes.MapHttpHandler(name, url, null, null, handlerFactory);
		}

		public static Route MapHttpHandler<THandler>(this RouteCollection routes, string name, string url, object defaults, object constraints, Func<RequestContext, THandler> handlerFactory) where THandler : IHttpHandler
		{
			var route = new Route(url, new HttpHandlerRouteHandler<THandler>(handlerFactory))
			{
				Defaults = new RouteValueDictionary(defaults),
				Constraints = new RouteValueDictionary(constraints)
			};
			routes.Add(name, new NormalizeRoute(route));
			route.SetRouteName(name);
			return route;
		}

		public static Route MapHttpHandler(this RouteCollection routes, string name, string url, IHttpHandler httpHandler)
		{
			return routes.MapHttpHandler(httpHandler, name, url, defaults: null, constraints: null);
		}

		public static Route MapHttpHandler(this RouteCollection routes, IHttpHandler httpHandler, string name, string url, object defaults, object constraints)
		{
			var route = new Route(url, new HttpHandlerRouteHandler(httpHandler))
			{
				Defaults = new RouteValueDictionary(defaults),
				Constraints = new RouteValueDictionary(constraints)
			};
			routes.Add(name, new NormalizeRoute(route));
			route.SetRouteName(name);
			return route;
		}

		public static Route Map(this RouteCollection routes, string name, string url, IRouteHandler routeHandler)
		{
			var route = new Route(url, routeHandler);
			routes.Add(name, new NormalizeRoute(route));
			route.SetRouteName(name);
			return route;
		}

		public static Route MapDelegate(this RouteCollection routes, string name, string url, Action<RequestContext> handler)
		{
			return routes.MapHttpHandler(name, url, null, null, requestContext => new DelegateHttpHandler(handler, requestContext.RouteData, false));
		}

		public static Route MapDelegate(this RouteCollection routes, string name, string url, object constraints, Action<RequestContext> handler)
		{
			return routes.MapHttpHandler(name, url, null, constraints, requestContext => new DelegateHttpHandler(handler, requestContext.RouteData, false));
		}

		public static string GetRouteName(this Route route)
		{
			if (route == null)
			{
				return null;
			}
			return route.DataTokens.GetRouteName();
		}

		public static string GetRouteName(this RouteData routeData)
		{
			if (routeData == null)
			{
				return null;
			}

			return routeData.DataTokens.GetRouteName();
		}

		public static Route SetRouteName(this Route route, string routeName)
		{
			if (route == null)
			{
				throw new ArgumentNullException("route");
			}
			if (route.DataTokens == null)
			{
				route.DataTokens = new RouteValueDictionary();
			}
			route.DataTokens.SetRouteName(routeName);
			return route;
		}

		// We always want to map the RedirectRoute *BEFORE* the legacy route that we're going to redirect.
		// Otherwise the redirect route will never match because the legacy route will supersede it. 
		// Hence the Func<RouteCollection, RouteBase>.
		public static RedirectRoute Redirect(this RouteCollection routes, Func<RouteCollection, RouteBase> routeMapping, bool permanent = false)
		{
			if (routes == null)
			{
				throw new ArgumentNullException("routes");
			}
			if (routeMapping == null)
			{
				throw new ArgumentNullException("routeMapping");
			}

			var routeCollection = new RouteCollection();
			var legacyRoute = routeMapping(routeCollection);

			var redirectRoute = new RedirectRoute(legacyRoute, null, permanent, null);
			routes.Add(new NormalizeRoute(redirectRoute));
			return redirectRoute;
		}

		public static Route Redirect(this RouteCollection routes, string name, string url, string to, bool permanent = false)
		{
			return routes.Map(name, url, new RedirectHttpHandler(to, permanent, isReusable: false));
		}
	}
}
